package list;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class LinkedListImplementation<E> implements LinkedList<E> {

    private class Node {
        private E data;
        private Node next;
        private Node previous;

        Node(E data) {
            this.data = data;
        }
    }

    private Node first;
    private Node last;
    private int size;

    private class IteratorImpl implements Iterator<E> {
        private Node currentNode = null;
        private boolean removeFlag = false;

        public boolean hasNext() {
            if (currentNode == null)
                return first != null;
            return currentNode.next != null;
        }

        public E next() {
            if (this.hasNext()) {
                if (currentNode == null)
                    currentNode = first;
                else
                    currentNode = currentNode.next;
                removeFlag = true;
                return currentNode.data;
            }
            throw new NoSuchElementException(
                    "No next element in the collection");
        }

        public void remove() {
            if (removeFlag) {
                currentNode.data = null;
                if (currentNode == first) {
                    first = currentNode.next;
                    currentNode.next = null;
                    currentNode = null;
                } else if (currentNode == last) {
                    last = currentNode.previous;
                    last.next = null;
                    currentNode = last;
                } else {
                    currentNode.previous.next = currentNode.next;
                    currentNode.next.previous = currentNode.previous;
                    currentNode = currentNode.previous;
                }
                removeFlag = false;
                size--;
            } else {
                throw new IllegalStateException(
                        "Invoke next() before invoking remove()");
            }
        }
    }

    @Override
    public void add(E e) {
        Node newNode = new Node(e);
        if (first == null) {
            first = newNode;
        } else {
            last.next = newNode;
            newNode.previous = last;
        }
        last = newNode;
        size++;
    }

    @Override
    public E getFirst() {
        if (first == null)
            return null;
        return first.data;
    }

    @Override
    public void clear() {
        Iterator<E> iterator = new IteratorImpl();
        while (iterator.hasNext()) {
            iterator.next();
            iterator.remove();
        }
    }

    @Override
    public boolean remove(Object o) {
        if (o == null)
            return false;
        IteratorImpl iterator = new IteratorImpl();
        while (iterator.hasNext()) {
            if (iterator.next().equals(o)) {
                iterator.remove();
                return true;
            }
        }
        return false;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean contains(Object o) {
        if (o == null)
            return false;
        IteratorImpl iterator = new IteratorImpl();
        while (iterator.hasNext()) {
            if (iterator.next().equals(o)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Iterator<E> iterator() {
        return new IteratorImpl();
    }
}
